#define NUM_TH 8
// #include "allocateur_sem.h"
// #include "lect-redac_moniteur.h"
#define _MULTI_THREADED
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#define MAX_LECT 2
#define NUM_REPET 2
#define MAX 10

sem_t sem;
pthread_mutex_t mutex_alloc = PTHREAD_MUTEX_INITIALIZER;
int nblect_attente = 0;
int nblect = 0; //readers executing
int nbredac_attente = 0;
int nbredac = 0; //writers executing 
pthread_cond_t lecteurs = PTHREAD_COND_INITIALIZER;
pthread_cond_t redacteurs = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex_lr = PTHREAD_MUTEX_INITIALIZER;

void allouer(int n, int idth) {
	pthread_mutex_lock(&mutex_alloc);
                int i;
                for(i = 0; i < n; i++) {
                        sem_wait(&sem);
                        printf("Thread %d is allocating resources (already allocated: %d)...\n", idth, i+1);
                }
        pthread_mutex_unlock(&mutex_alloc);
}

void liberer(int n, int idth) {
        int i;
        for(i = 0; i < n; i++) {
                sem_post(&sem);
                printf("Thread %d is liberating resources (already liberated: %d)...\n", idth, i+1);
        }
        printf("Thread %d liberated %d resources...\n", idth, n);       
}

void debut_lire(int id)
{
	pthread_mutex_lock(&mutex_lr);
		printf("debut_lire %d\n", id);
 		nblect_attente++;
		while(nbredac > 0 || nbredac_attente > 0){
			printf("lecteur %d va attendre\n", id);
			pthread_cond_wait(&lecteurs, &mutex_lr);
			printf("lecteur %d est reveille\n", id);
			pthread_cond_broadcast(&lecteurs);
		}
		nblect_attente --;
		nblect++;
	pthread_mutex_unlock(&mutex_lr);
}

void fin_lire(int id)
{
	pthread_mutex_lock(&mutex_lr);
		printf("fin_lire %d\n", id);
		nblect--;
		if(nblect == 0){
			printf("lecteur %d va reveiller les redacteurs\n", id);
			pthread_cond_broadcast(&redacteurs);
		}
	pthread_mutex_unlock(&mutex_lr);
}

void debut_ecrire(int id)
{
	pthread_mutex_lock(&mutex_lr);
		printf("debut ecrire %d\n", id);
		nbredac_attente++;
		while(nblect > 0 || nbredac > 0){
			printf("redacteur %d va attendre\n", id);
			pthread_cond_wait(&redacteurs, &mutex_lr);
			printf("redacteur %d est reveille\n", id);
		}
		nbredac = 1;
		nbredac_attente--;
	pthread_mutex_unlock(&mutex_lr);
}

void fin_ecrire(int id)
{
	pthread_mutex_lock(&mutex_lr);
		printf("fin ecrire %d\n", id);
		nbredac = 0; 
		if(nbredac_attente > 0){
			printf("redacteur %d va reveiller les autres redacteurs en attendre\n", id);
			pthread_cond_broadcast(&redacteurs);
		}
		else{
			printf("redacteur %d va reveiller les lecteurs en attendre\n", id);
			pthread_cond_broadcast(&lecteurs);
		}
	pthread_mutex_unlock(&mutex_lr);
}

void* thread1(void *id)
{
	int tid = (int) id;
	for(int i = 0; i < 10; i++)
	{
		//fonctions moniteur: lect-redac
		debut_lire(tid);
		fin_lire(tid);
		usleep(100 * drand48());
		// fonctions liees probleme en semaphore: allocateur
		int v = 10 * drand48();
                allouer(v, tid);
                liberer(v, tid);
	}
	return NULL;
}

void* thread2(void *id)
{
        int tid = (int) id;
        for(int i = 0; i < 10; i++)
        {
                //fonctions moniteur: lect-redac
                debut_ecrire(tid);
                fin_lire(tid);
                usleep(100 * drand48());
		// fonctions semaphore: allocateur
                int v = 10 * drand48();
                allouer(v, tid);
                liberer(v, tid);
        }
	return NULL;
}

int main()
{
  pthread_t threads[NUM_TH];
  int rc;
  
  sem_init(&sem, 0, MAX);
  
  for(int i = 0; i < NUM_TH/2; i++){
    rc = pthread_create(&threads[i], NULL, thread1, (void *)i);
    if(rc){
      perror("erreur thd redac");
      exit(-1);
    }
    rc = pthread_create(&threads[i], NULL, thread2, (void *)i);
    if(rc){
      perror("erreur thd redac");
      exit(-1);
    }    
  } 
  
  sem_destroy(&sem);
  pthread_mutex_destroy(&mutex_alloc);
  pthread_mutex_destroy(&mutex_lr);
  pthread_exit(NULL);
}
